home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3.2 / Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO / packet / n17jsrc / popserv.c < prev    next >
C/C++ Source or Header  |  1991-06-03  |  15KB  |  719 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #include <sys/stat.h>
  12. #ifdef UNIX
  13. #include <sys/types.h>
  14. #endif
  15. #if    defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27.  
  28. extern char Nospace[];
  29.  
  30. static struct pop_scb *create_scb __ARGS((void));
  31. static void delete_scb __ARGS((struct pop_scb *scb));
  32. static void popserv __ARGS((int s,void *unused,void *p));
  33. static int poplogin __ARGS((char *pass,char *username));
  34.  
  35. static int Spop = -1; /* prototype socket for service */
  36.  
  37. /* Start up POP receiver service */
  38. int
  39. pop1(argc,argv,p)
  40.  
  41. int argc;
  42. char *argv[];
  43. void *p;
  44.  
  45. {
  46.     struct sockaddr_in lsocket;
  47.     int s;
  48.  
  49.     if (Spop != -1) {
  50.         return 0;
  51.     }
  52.  
  53.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  54.     chname(Curproc,"POP listener");
  55.  
  56.     lsocket.sin_family = AF_INET;
  57.     lsocket.sin_addr.s_addr = INADDR_ANY;
  58.     if(argc < 2)
  59.         lsocket.sin_port = IPPORT_POP;
  60.     else
  61.         lsocket.sin_port = atoi(argv[1]);
  62.  
  63.     Spop = socket(AF_INET,SOCK_STREAM,0);
  64.  
  65.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  66.  
  67.     listen(Spop,1);
  68.  
  69.     for (;;) {
  70.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  71.             break;    /* Service is shutting down */
  72.  
  73.         /* Spawn a server */
  74.  
  75.         newproc("POP server",2048,popserv,s,NULL,NULL,0);
  76.     }
  77.     return 0;
  78. }
  79.  
  80. /* Shutdown POP service (existing connections are allowed to finish) */
  81.  
  82. int
  83. pop0(argc,argv,p)
  84. int argc;
  85. char *argv[];
  86. void *p;
  87.  
  88. {
  89.     close_s(Spop);
  90.     Spop = -1;
  91.     return 0;
  92. }
  93.  
  94. static void
  95. popserv(s,unused,p)
  96. int s;
  97. void *unused;
  98. void *p;
  99. {
  100.     void pop_sm();
  101.     struct pop_scb *scb;
  102.  
  103.     sockowner(s,Curproc);        /* We own it now */
  104.     log(s,"open POP");
  105.  
  106.     if((scb = create_scb()) == NULLSCB) {
  107.         tprintf(Nospace);
  108.         log(s,"close POP - no space");
  109.         close_s(s);
  110.         return;
  111.     }
  112.  
  113.     scb->socket = s;
  114.     scb->state  = AUTH;
  115.  
  116.     (void) usprintf(s,greeting_msg,Hostname);
  117.  
  118. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  119.         /* He closed on us */
  120.  
  121.         goto quit;
  122.     }
  123.  
  124.     rip(scb->buf);
  125.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  126.         goto loop;
  127.     pop_sm(scb);
  128.     if (scb->state == DONE)
  129.         goto quit;
  130.  
  131.     goto loop;
  132.  
  133. quit:
  134.     log(scb->socket,"close POP");
  135.     close_s(scb->socket);
  136.     delete_scb(scb);
  137. }
  138.  
  139.  
  140. /* Create control block, initialize */
  141.  
  142. static struct
  143. pop_scb *create_scb()
  144. {
  145.     register struct pop_scb *scb;
  146.  
  147.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  148.         return NULLSCB;
  149.  
  150.     scb->username[0] = '\0';
  151.     scb->msg_status = NULL;
  152.     scb->wf = NULL;
  153.  
  154.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  155.  
  156.     scb->folder_modified = FALSE;
  157.     return scb;
  158. }
  159.  
  160.  
  161. /* Free resources, delete control block */
  162.  
  163. static void
  164. delete_scb(scb)
  165. register struct pop_scb *scb;
  166. {
  167.  
  168.     if (scb == NULLSCB)
  169.         return;
  170.     if (scb->wf != NULL)
  171.         fclose(scb->wf);
  172.     if (scb->msg_status  != NULL)
  173.         free((char *)scb->msg_status);
  174.  
  175.     free((char *)scb);
  176. }
  177.  
  178. /* replace terminating end of line marker(s) (\r and \n) with null */
  179. void
  180. rrip(s)
  181. register char *s;
  182. {
  183.     register char *cp;
  184.  
  185.     if((cp = strchr(s,'\r')) != NULLCHAR)
  186.         *cp = '\0';
  187.     if((cp = strchr(s,'\n')) != NULLCHAR)
  188.         *cp = '\0';
  189. }
  190.  
  191. /* --------------------- start of POP server code ------------------------ */
  192.  
  193. #define    BITS_PER_WORD        16
  194.  
  195. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  196.  
  197. /* Command string specifications */
  198.  
  199. static char    ackd_cmd[] = "ACKD",
  200.         acks_cmd[] = "ACKS",
  201. #ifdef POP_FOLDERS
  202.         fold_cmd[] = "FOLD ",
  203. #endif
  204.         login_cmd[] = "HELO ",
  205.         nack_cmd[] = "NACK",
  206.         quit_cmd[] = "QUIT",
  207.         read_cmd[] = "READ",
  208.         retr_cmd[] = "RETR";
  209.  
  210. void
  211. pop_sm(scb)
  212. struct pop_scb *scb;
  213. {
  214.     char password[40];
  215.     void state_error(struct pop_scb *,char *);
  216.     void open_folder(struct pop_scb *);
  217.     void do_cleanup(struct pop_scb *);
  218.     void read_message(struct pop_scb *);
  219.     void retrieve_message(struct pop_scb *);
  220.     void deletemsg(struct pop_scb *,int);
  221.     void get_message(struct pop_scb *,int);
  222.     void print_message_length(struct pop_scb *);
  223.     void close_folder(struct pop_scb *);
  224. #ifdef POP_FOLDERS
  225.     void select_folder(struct pop_scb *);
  226. #endif
  227.  
  228.     if(scb == NULLSCB)
  229.         return;
  230.     switch(scb->state) {
  231.     case AUTH:
  232.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  233.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  234.  
  235.             if (!poplogin(scb->username,password)) {
  236.                 log(scb->socket,"POP access DENIED to %s",
  237.                         scb->username);
  238.                 state_error(scb,"Access DENIED!!");
  239.                 return;
  240.             }
  241.  
  242.             log(scb->socket,"POP access granted to %s",
  243.                     scb->username);
  244.             open_folder(scb);
  245.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  246.             do_cleanup(scb);
  247.         } else
  248.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  249.         break;
  250.  
  251.     case MBOX:
  252.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  253.             read_message(scb);
  254.  
  255. #ifdef POP_FOLDERS
  256.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  257.             select_folder(scb);
  258.  
  259. #endif
  260.  
  261.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  262.             do_cleanup(scb);
  263.         } else
  264.             state_error(scb,
  265. #ifdef POP_FOLDERS
  266.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  267. #else
  268.                     "(MBOX) Expected READ or QUIT command");
  269. #endif
  270.         break;
  271.  
  272.     case ITEM:
  273.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  274.             read_message(scb);
  275.  
  276. #ifdef POP_FOLDERS
  277.  
  278.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  279.             select_folder(scb);
  280. #endif
  281.  
  282.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  283.             retrieve_message(scb);
  284.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  285.             do_cleanup(scb);
  286.         else
  287.             state_error(scb,
  288. #ifdef POP_FOLDERS
  289.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  290. #else
  291.                "(ITEM) Expected READ, RETR, or QUIT command");
  292. #endif
  293.         break;
  294.  
  295.     case NEXT:
  296.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  297.                 /* ACKD processing */
  298.             deletemsg(scb,scb->msg_num);
  299.             scb->msg_num++;
  300.             get_message(scb,scb->msg_num);
  301.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  302.                 /* ACKS processing */
  303.             scb->msg_num++;
  304.             get_message(scb,scb->msg_num);
  305.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  306.                 /* NACK processing */
  307.             fseek(scb->wf,scb->curpos,SEEK_SET);
  308.         } else {
  309.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  310.             return;
  311.         }
  312.  
  313.         print_message_length(scb);
  314.         scb->state  = ITEM;
  315.         break;
  316.  
  317.     case DONE:
  318.         do_cleanup(scb);
  319.         break;
  320.  
  321.     default:
  322.         state_error(scb,"(TOP) State Error!!");
  323.         break;
  324.     }
  325. }
  326.  
  327. void
  328. do_cleanup(scb)
  329. struct pop_scb *scb;
  330. {
  331.     void close_folder __ARGS((struct pop_scb *));
  332.     close_folder(scb);
  333.     (void) usprintf(scb->socket,signoff_msg);
  334.     scb->state = DONE;
  335. }
  336.  
  337. void
  338. state_error(scb,msg)
  339. struct pop_scb *scb;
  340. char *msg;
  341. {
  342.     (void) usprintf(scb->socket,error_rsp,msg);
  343.     scb->state = DONE;
  344. }
  345.  
  346. #ifdef POP_FOLDERS
  347.  
  348. select_folder(scb)
  349. struct pop_scb    *scb;
  350. {
  351.     sscanf(scb->buf,"FOLD %s",scb->username);
  352.  
  353.     if (scb->wf != NULL)
  354.         close_folder(scb);
  355.  
  356.     open_folder(scb);
  357. }
  358.  
  359. #endif
  360.  
  361.  
  362. void
  363. close_folder(scb)
  364. struct pop_scb *scb;
  365. {
  366.     char folder_pathname[64];
  367.     char line[BUF_LEN];
  368.     FILE *fd;
  369.     int deleted = FALSE;
  370.     int msg_no = 0;
  371.     struct stat folder_stat;
  372.     int newmail(struct pop_scb *);
  373.     int isdeleted(struct pop_scb *,int);
  374.  
  375.     if (scb->wf == NULL)
  376.         return;
  377.  
  378.     if (!scb->folder_modified) {
  379.         /* no need to re-write the folder if we have not modified it */
  380.  
  381.         fclose(scb->wf);
  382.         scb->wf = NULL;
  383.  
  384.         free((char *)scb->msg_status);
  385.         scb->msg_status = NULL;
  386.         return;
  387.     }
  388.  
  389.  
  390.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  391.  
  392.     if (newmail(scb)) {
  393.         /* copy new mail into the work file and save the
  394.            message count for later */
  395.  
  396.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  397.             state_error(scb,"Unable to add new mail to folder");
  398.             return;
  399.         }
  400.  
  401.         fseek(scb->wf,0,SEEK_END);
  402.         fseek(fd,scb->folder_file_size,SEEK_SET);
  403.         while (!feof(fd)) {
  404.             fgets(line,BUF_LEN,fd);
  405.             fputs(line,scb->wf);
  406.         }
  407.  
  408.         fclose(fd);
  409.     }
  410.  
  411.     /* now create the updated mail folder */
  412.  
  413.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  414.         state_error(scb,"Unable to update mail folder");
  415.         return;
  416.     }
  417.  
  418.     rewind(scb->wf);
  419.     while (!feof(scb->wf)){
  420.         fgets(line,BUF_LEN,scb->wf);
  421.  
  422.         if (isSOM(line)){
  423.             msg_no++;
  424.             if (msg_no <= scb->folder_len)
  425.                 deleted = isdeleted(scb,msg_no);
  426.             else
  427.                 deleted = FALSE;
  428.         }
  429.  
  430.         if (deleted)
  431.             continue;
  432.  
  433.         fputs(line,fd);
  434.     }
  435.  
  436.     fclose(fd);
  437.  
  438.     /* trash the updated mail folder if it is empty */
  439.  
  440.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  441.         unlink(folder_pathname);
  442.  
  443.     fclose(scb->wf);
  444.     scb->wf = NULL;
  445.  
  446.     free((char *)scb->msg_status);
  447.     scb->msg_status = NULL;
  448. }
  449.  
  450. void
  451. open_folder(scb)
  452. struct pop_scb    *scb;
  453. {
  454.     char folder_pathname[64];
  455.     char line[BUF_LEN];
  456.     FILE *fd;
  457.     FILE *tmpfile();
  458.     struct stat folder_stat;
  459.  
  460.  
  461.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  462.     scb->folder_len       = 0;
  463.     scb->folder_file_size = 0;
  464.     if (stat(folder_pathname,&folder_stat)){
  465.          (void) usprintf(scb->socket,no_mail_rsp);
  466.          return;
  467.     }
  468.  
  469.     scb->folder_file_size = folder_stat.st_size;
  470.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  471.         state_error(scb,"Unable to open mail folder");
  472.         return;
  473.     }
  474.  
  475.     if ((scb->wf = tmpfile()) == NULL) {
  476.         state_error(scb,"Unable to create work folder");
  477.         return;
  478.     }
  479.  
  480.     while(!feof(fd)) {
  481.         fgets(line,BUF_LEN,fd);
  482.  
  483.         /* scan for begining of a message */
  484.  
  485.         if (isSOM(line))
  486.             scb->folder_len++;
  487.  
  488.         /* now put  the line in the work file */
  489.  
  490.         fputs(line,scb->wf);
  491.     }
  492.  
  493.     fclose(fd);
  494.  
  495.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  496.  
  497.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  498.         (scb->msg_status_size == 0))
  499.         scb->msg_status_size++;
  500.  
  501.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  502.                 sizeof(unsigned int))) == NULL) {
  503.         state_error(scb,"Unable to create message status array");
  504.         return;
  505.     }
  506.  
  507.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  508.  
  509.     scb->state  = MBOX;
  510. }
  511.  
  512. void
  513. read_message(scb)
  514. struct pop_scb    *scb;
  515. {
  516.     void get_message __ARGS((struct pop_scb *,int ));
  517.     void print_message_length __ARGS((struct pop_scb *));
  518.  
  519.     if(scb == NULLSCB)
  520.         return;
  521.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  522.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  523.     else
  524.         scb->msg_num++;
  525.  
  526.     get_message(scb,scb->msg_num);
  527.     print_message_length(scb);
  528.     scb->state  = ITEM;
  529. }
  530.  
  531. void
  532. retrieve_message(scb)
  533. struct pop_scb    *scb;
  534. {
  535.     char line[BUF_LEN];
  536.     long cnt;
  537.     void rrip(char *);
  538.  
  539.     if(scb == NULLSCB)
  540.         return;
  541.     if (scb->msg_len == 0) {
  542.         state_error(scb,"Attempt to access a DELETED message!");
  543.         return;
  544.     }
  545.  
  546.     cnt  = scb->msg_len;
  547.     while(!feof(scb->wf) && (cnt > 0)) {
  548.         fgets(line,BUF_LEN,scb->wf);
  549.         rrip(line);
  550.  
  551.         (void) usprintf(scb->socket,msg_line,line);
  552.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  553.     }
  554.  
  555.     scb->state = NEXT;
  556. }
  557.  
  558. void
  559. get_message(scb,msg_no)
  560. struct pop_scb    *scb;
  561. int msg_no;
  562. {
  563.     char line[BUF_LEN];
  564.     long ftell();
  565.     void rrip(char *);
  566.  
  567.     if(scb == NULLSCB)
  568.         return;
  569.     scb->msg_len = 0;
  570.     if (msg_no > scb->folder_len) {
  571.         scb->curpos  = 0;
  572.         scb->nextpos = 0;
  573.         return;
  574.     } else {
  575.         /* find the message and its length */
  576.  
  577.         rewind(scb->wf);
  578.         while (!feof(scb->wf) && (msg_no > -1)) {
  579.             if (msg_no > 0)
  580.                 scb->curpos = ftell(scb->wf);
  581.             
  582.             fgets(line,BUF_LEN,scb->wf);
  583.             rrip(line);
  584.  
  585.             if (isSOM(line))
  586.                 msg_no--;
  587.  
  588.             if (msg_no != 0)
  589.                 continue;
  590.  
  591.             scb->nextpos  = ftell(scb->wf);
  592.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  593.         }
  594.     }
  595.  
  596.     if (scb->msg_len > 0)
  597.         fseek(scb->wf,scb->curpos,SEEK_SET);
  598.  
  599.     /* we need the pointers even if the message was deleted */
  600.  
  601.     if  (isdeleted(scb,scb->msg_num))
  602.         scb->msg_len = 0;
  603. }
  604.  
  605. static int
  606. poplogin(username,pass)
  607. char *pass;
  608. char *username;
  609. {
  610.     char buf[80];
  611.     char *cp;
  612.     char *cp1;
  613.     FILE *fp;
  614.  
  615.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  616.         /* User file doesn't exist */
  617.         tprintf("POP users file %s not found\n",Popusers);
  618.         return(FALSE);
  619.     }
  620.  
  621.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  622.         if(buf[0] == '#')
  623.             continue;    /* Comment */
  624.  
  625.         if((cp = strchr(buf,':')) == NULLCHAR)
  626.             /* Bogus entry */
  627.             continue;
  628.  
  629.         *cp++ = '\0';        /* Now points to password */
  630.         if(strcmp(username,buf) == 0)
  631.             break;        /* Found user name */
  632.     }
  633.  
  634.     if(feof(fp)) {
  635.         /* User name not found in file */
  636.  
  637.         fclose(fp);
  638.         return(FALSE);
  639.     }
  640.     fclose(fp);
  641.  
  642.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  643.         return(FALSE);
  644.  
  645.     *cp1 = '\0';
  646.     if(strcmp(cp,pass) != 0) {
  647.         /* Password required, but wrong one given */
  648.  
  649.         return(FALSE);
  650.     }
  651.  
  652.     /* whew! finally made it!! */
  653.  
  654.     return(TRUE);
  655. }
  656.  
  657. int
  658. isdeleted(scb,msg_no)
  659. struct pop_scb *scb;
  660. int msg_no;
  661. {
  662.     unsigned int mask = 1,offset;
  663.  
  664.     msg_no--;
  665.     offset = msg_no / BITS_PER_WORD;
  666.     mask <<= msg_no % BITS_PER_WORD;
  667.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  668. }
  669.  
  670. void
  671. deletemsg(scb,msg_no)
  672. struct pop_scb *scb;
  673. int msg_no;
  674. {
  675.     unsigned int mask = 1,offset;
  676.  
  677.     if(scb == NULLSCB)
  678.         return;
  679.     msg_no--;
  680.     offset = msg_no / BITS_PER_WORD;
  681.     mask <<= msg_no % BITS_PER_WORD;
  682.     scb->msg_status[offset] |= mask;
  683.     scb->folder_modified = TRUE;
  684. }
  685.  
  686. int
  687. newmail(scb)
  688. struct pop_scb *scb;
  689. {
  690.     char folder_pathname[64];
  691.     struct stat folder_stat;
  692.  
  693.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  694.  
  695.     if (stat(folder_pathname,&folder_stat)) {
  696.         state_error(scb,"Unable to get old mail folder's status");
  697.         return(FALSE);
  698.     } else
  699.         return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
  700. }
  701.  
  702. void
  703. print_message_length(scb)
  704. struct pop_scb *scb;
  705. {
  706.     char *print_control_string;
  707.  
  708.     if(scb == NULLSCB)
  709.         return;
  710.     if (scb->msg_len > 0)
  711.         print_control_string = length_rsp;
  712.     else if (scb->msg_num <= scb->folder_len)
  713.         print_control_string = length_rsp;
  714.     else
  715.         print_control_string = no_more_rsp;
  716.  
  717.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  718. }
  719.